import json
import logging
import os
import time
import traceback
from multiprocessing import Queue, Lock

import urllib

import prettytable
import psutil
import requests

from automonkey.config import DefaultConfig
from .case import Case
from .exception import CaseTypeErrorException, FileDownloadErrorException
from .monkey_runner import MonkeyRunner
from .performance_runner import PerformanceRunner
from .tcloud_update import TCloud
from .utils import Utils

logger = logging.getLogger(__name__)

"""
#  Runer
#  运行 Case
"""


class Runner(object):

    def __init__(self):
        self.queue = None
        self.lock = None
        self.tcloud = None
        pass

    def show_system_information(self):
        pass

    def run(self, case):
        try:
            if not isinstance(case, Case):
                logger.error('Need Case Here, not {}'.format(type(case)))
                raise CaseTypeErrorException

            self.show_system_information()

            # 测试开始时间
            case.result.on_case_begin()
            logger.info('Case is {}'.format(case.case_type))
            self.setup(case)

            if case.case_type == 1:
                self.run_monkeys(case.cases)
            elif case.case_type == 2:
                self.run_performance(case.cases,ext=case)


        except Exception as e:
            logger.error(e)
            traceback.print_exc()

    def run_monkeys(self, monkeys):
        try:
            if not isinstance(monkeys, list):
                logger.error('Need list Here, not {}'.format(type(monkeys)))
                raise TypeError

            self.tcloud.on_monkey_begin(os.environ.get('BUILD_URL'))

            process_list = []
            for monkey in monkeys:
                process = MonkeyRunner(self.queue, self.lock, monkey)
                process_list.append(process)

            for process in process_list:
                process.start()

            table_process = prettytable.PrettyTable()
            table_process.field_names = ["process id", "device id"]
            for process in process_list:
                row = [process.pid, process.monkey.device.device_id]
                table_process.add_row(row)
            logger.info('\n{}'.format(table_process))

            results = []
            while True:
                if len(results) == len(process_list):
                    break
                if not self.queue.empty():
                    results.append(self.queue.get())

            for result in results:
                print(result.info)

        except FileDownloadErrorException as f_e:
            logger.error(f_e)
            logger.error(traceback.format_exc())
            self.tcloud.on_download_app(False)
        except Exception as e:
            logger.error(e)
            traceback.print_exc()
        finally:
            time.sleep(3)
            self.tcloud.on_monkey_end()

    # ext -补充参数，额外的数据
    def run_performance(self, performances,ext=None):
        try:
            if not isinstance(performances, list):
                logger.error('Need list Here, not {}'.format(type(performances)))
                raise TypeError

            self.tcloud.on_monkey_begin(os.environ.get('BUILD_URL'))

            config_json = -1
            if len(performances) > 0:
                config_json = self.download_script(performances[0])
            if config_json == -1:
                return

            start_time = time.time()
            run_time = (performances[0].config.run_time + 10) * 60  # 外部超时时间比预定的多10分钟，让进程尝试自己超时退出，不行再外部处理

            process_list = []
            for performance in performances:
                performance.config.config_json = config_json
                process = PerformanceRunner(self.queue, self.lock, performance,ext=ext)
                process_list.append(process)

            for process in process_list:
                process.start()

            table_process = prettytable.PrettyTable()
            table_process.field_names = ["process id", "device id"]
            for process in process_list:
                row = [process.pid, process.performance.device.device_id]
                table_process.add_row(row)
            logger.info('\n{}'.format(table_process))

            results = []
            process_list2 = process_list
            while True:
                if len(results) == len(process_list):
                    break
                if not self.queue.empty():
                    results.append(self.queue.get())

                current_time = time.time()
                if (current_time - start_time) > run_time:
                    p_list = process_list2
                    for process in p_list:
                        if process.is_alive():
                            logger.info("current process.is_alive() pid = {}".format(process.pid))
                            process.timeout_exit(2, "已超时，强制结束进程")
                            process_list2.remove(process)
                time.sleep(2)

            for result in results:
                print(result.info)

        except FileDownloadErrorException as f_e:
            logger.error(f_e)
            logger.error(traceback.format_exc())
            self.tcloud.on_download_app(False)
        except Exception as e:
            logger.error(e)
            traceback.print_exc()
        finally:
            for performance in performances:
                self.tcloud.release_monkey_device(performance.device.device_id)
            time.sleep(5)
            self.tcloud.on_monkey_end()

    # 初始化
    def setup(self, case):
        try:
            if not isinstance(case, Case):
                logger.error('Need Case Here, not {}'.format(type(case)))
                raise CaseTypeErrorException

            self.tcloud = TCloud(0, 0, monkey_id=case.monkey_id, tcloud_url=case.tcloud_url)

            # 下载文件
            download_package = Utils.download_apk_from_url(case.app_download_url, DefaultConfig.LOCAL_APP_PATH, None)

            self.tcloud.on_download_app(True)
            case.bind_local_package_to_monkey(download_package)

            # 初始化 queue 用于多进程通信
            self.queue = Queue(-1)
            # 初始化 lock 用于 多进程
            self.lock = Lock()

            return download_package

        except Exception as e:
            logger.error(e)
            traceback.print_exc()
            raise e

    # 下载脚本
    def download_script(self, performance):
        logger.info("脚本下载连接: {}".format(performance.config.test_envs))
        if performance.config.test_envs == "":
            logger.error("脚本下载路径不存在")
            return -1
        url = performance.config.test_envs
        # try:
        #     url = urllib.parse.unquote(url, encoding='utf-8', errors='replace')
        # except:
        #     pass
        baseurl, file_name = os.path.split(url)
        if("?" in file_name):
            file_name = file_name[0:file_name.index("?")]
        logger.info("url = {},baseurl ={} ,file_name = {}".format(url,baseurl, file_name))
        script_folder = DefaultConfig.LOCAL_AIRTEST_SCRIPT_PATH

        if os.path.exists(script_folder):
            logger.info("删除重建 script 目录")
            p = Utils.command_execute('rm -rf {}'.format(script_folder))
            p.communicate()

        if not os.path.exists(script_folder):
            logger.info("初始化 script 目录")
            os.makedirs(script_folder)
        logger.info("脚本名称 = {}".format(file_name))
        script_zip = os.path.join(script_folder, file_name)
        logger.info("下载脚本地址 = {}".format(script_zip))
        r = requests.get(url)
        with open(script_zip, "wb") as f:
            f.write(r.content)
            f.flush()
            f.close()
        logger.info("下载脚本完成")
        logger.info("脚本大小 = {}".format(os.path.getsize(script_zip)))

        if file_name.endswith("zip"):
            Utils.unpack_zip(script_zip, script_folder)
        elif file_name.endswith("rar"):
            Utils.unpack_rar(script_zip, script_folder)
        else:
            logger.error("下载的文件需要是zip或rar")
            return -1

        config_json_file = [name for name in os.listdir(script_folder) if name.endswith(".json")]
        logger.info("config.json file = {}".format(config_json_file))
        if type(config_json_file) is list:
            config_json_file = config_json_file[0]

        with open(os.path.join(script_folder, config_json_file), "r") as f:
            config_json = json.loads(f.read())
            f.close()
        config_json["root"] = script_folder
        logger.info("config.json = {}".format(config_json))

        return config_json
